﻿// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Collections;
using System.ComponentModel;
using System.Windows.Forms.Design.Behavior;

namespace System.Windows.Forms.Design;

/// <summary>
///  Provides a designer that can design components
///  that extend TextBoxBase.
/// </summary>
internal class TextBoxBaseDesigner : ControlDesigner
{
    public TextBoxBaseDesigner()
    {
        AutoResizeHandles = true;
    }

    /// <summary>
    ///  Adds a baseline SnapLine to the list of SnapLines related
    ///  to this control.
    /// </summary>
    public override IList SnapLines
    {
        get
        {
            int baseline = DesignerUtils.GetTextBaseline(Control, Drawing.ContentAlignment.TopLeft);
            BorderStyle borderStyle = BorderStyle.Fixed3D;
            PropertyDescriptorCollection props = TypeDescriptor.GetProperties(Component);
            props.TryGetPropertyDescriptorValue(
                "BorderStyle",
                Component,
                ref borderStyle);

            if (borderStyle == BorderStyle.None)
            {
                baseline += 0;
            }
            else if (borderStyle == BorderStyle.FixedSingle)
            {
                baseline += 2;
            }
            else if (borderStyle == BorderStyle.Fixed3D)
            {
                baseline += 3;
            }
            else
            {
                Debug.Fail("Unknown borderstyle");
                baseline += 0;
            }

            IList<SnapLine> snapLines = SnapLinesInternal;
            snapLines.Add(new SnapLine(SnapLineType.Baseline, baseline, SnapLinePriority.Medium));
            return snapLines.Unwrap();
        }
    }

    private string Text
    {
        get => Control.Text;
        set
        {
            Control.Text = value;

            // This fixes bug #48462. If the text box is not wide enough to display all of the text,
            // then we want to display the first portion at design-time. We can ensure this by
            // setting the selection to (0, 0).
            ((TextBoxBase)Control).Select(0, 0);
        }
    }

    private bool ShouldSerializeText()
    {
        return TypeDescriptor.GetProperties(typeof(TextBoxBase))["Text"]!.ShouldSerializeValue(Component);
    }

    private void ResetText()
    {
        Control.Text = string.Empty;
    }

    /// <summary>
    ///  We override this so we can clear the text field set by controldesigner.
    /// </summary>
    /// <param name="defaultValues">The default values.</param>
    public override void InitializeNewComponent(IDictionary? defaultValues)
    {
        base.InitializeNewComponent(defaultValues);

        PropertyDescriptor? textProp = TypeDescriptor.GetProperties(Component)["Text"];
        if (textProp is not null && textProp.PropertyType == typeof(string) && !textProp.IsReadOnly && textProp.IsBrowsable)
        {
            textProp.SetValue(Component, string.Empty);
        }
    }

    protected override void PreFilterProperties(IDictionary properties)
    {
        base.PreFilterProperties(properties);

        // Handle shadowed properties
        string[] shadowProps =
        [
            "Text",
        ];

        for (int i = 0; i < shadowProps.Length; i++)
        {
            if (properties[shadowProps[i]] is PropertyDescriptor prop)
            {
                properties[shadowProps[i]] = TypeDescriptor.CreateProperty(typeof(TextBoxBaseDesigner), prop, []);
            }
        }
    }

    /// <summary>
    ///  Retrieves a set of rules concerning the movement capabilities of a component.
    ///  This should be one or more flags from the SelectionRules class. If no designer
    ///  provides rules for a component, the component will not get any UI services.
    /// </summary>
    public override SelectionRules SelectionRules
    {
        get
        {
            SelectionRules rules = base.SelectionRules;
            object component = Component;

            rules |= SelectionRules.AllSizeable;

            PropertyDescriptor? prop = TypeDescriptor.GetProperties(component)["Multiline"];

            object? value = prop?.GetValue(component);
            if (value is bool valueAsBool && !valueAsBool)
            {
                PropertyDescriptor? propAuto = TypeDescriptor.GetProperties(component)["AutoSize"];
                object? auto = propAuto?.GetValue(component);
                if (auto is bool autoAsBool && autoAsBool)
                {
                    rules &= ~(SelectionRules.TopSizeable | SelectionRules.BottomSizeable);
                }
            }

            return rules;
        }
    }
}
